﻿using MediatR;
using Service.Core.Repository.Core;
using Service.Repository;
using SqlSugar;
using System.Linq.Expressions;
using System.Reflection;

namespace Service.Core.Repository
{
    public class Repository<TEntity>
        : IRepository<TEntity>
        where TEntity : Entity, new()
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="memoryCache"></param>
        public Repository(ISqlSugarClient dbContext, IMediator mediator)
        {
            _mediator = mediator;
            _dbBase = dbContext as SqlSugarClient;
        }

        private readonly IMediator _mediator;
        private readonly SqlSugarClient _dbBase;

        internal ISqlSugarClient _dbContext
        {
            get
            {
                String configId = DbTenantEnum.Default.ToString(); //定义默认配置
                ISqlSugarClient db = _dbBase;
                var iTenant = db.AsTenant();
                var entityType = typeof(TEntity);
                if (entityType.IsDefined(typeof(TenantAttribute), false))
                {
                    var tenantAttribute = entityType.GetCustomAttribute<TenantAttribute>(false)!;
                    configId = tenantAttribute.configId.ToString();
                    // return iTenant.GetConnectionScope(tenantAttribute.configId);
                }
                else
                {
                    //获取租户信息 租户信息可以提前缓存下来 
                    //var tenants = _memoryCache.Get<List<TbTenant>>(typeof(TbTenant));
                    //if (tenants == null)
                    //{
                    //    tenants = db.Queryable<TbTenant>().ToList();
                    //    _memoryCache.Set(typeof(TbTenant), tenants, TimeSpan.FromMinutes(30));
                    //}
                    //if (tenants != null && tenants.Count > 0)
                    //{
                    //    var user = ManualServiceLocator.Instance.GetService<IUserSession>();
                    //    var tenant = tenants.Where(s => s.Id == user.TenantId || s.Id == TenantId).FirstOrDefault();
                    //    if (tenant is not null && tenant.IsSplitDatas)
                    //    {
                    //        //空连接地址，使用默认租户标识
                    //        if (String.IsNullOrWhiteSpace(tenant.SqlConnection))
                    //            configId = DBEnum.Default;
                    //        else if (!iTenant.IsAnyConnection(tenant.Id))
                    //        {
                    //            configId = tenant.Id;
                    //            iTenant.AddConnection(new ConnectionConfig()
                    //            {
                    //                ConfigId = tenant.Id,
                    //                ConnectionString = tenant.SqlConnection,
                    //                DbType = tenant.DbType,
                    //                IsAutoCloseConnection = true,
                    //                InitKeyType = InitKeyType.Attribute,
                    //            });
                    //        }
                    //    }
                    //}
                }

                var idb = iTenant.GetConnectionScope(configId);
                idb.QueryFilter.AddTableFilter<IDelete>(it => !it.IsDelete);
                idb.QueryFilter.AddTableFilter<IEntityTenant>(it => it.TenantId.Equals(configId));
                return idb;
            }

            set { }
        }

        public ISqlSugarClient Db => _dbContext;


        public Task<int> GetCount(Expression<Func<TEntity, bool>> whereExpression = null,
            CancellationToken cancellationToken = default)
        {
            var query = _dbContext.Queryable<TEntity>();
            if (whereExpression is not null)
                query = query.Where(whereExpression);
            return query.CountAsync();
        }

        public async Task<int> GetMaxAsync(Expression<Func<TEntity, int>> maxExpression,
            Expression<Func<TEntity, bool>>? whereExpression = null,
            CancellationToken cancellationToken = default)
        {
            var query = _dbContext.Queryable<TEntity>();
            if (whereExpression is not null)
                query = query.Where(whereExpression);
            return await query.MaxAsync<int>(maxExpression);
        }


        public async Task<TResult> GetMaxAsync<TResult>(Expression<Func<TEntity, TResult>> maxExpression,
    Expression<Func<TEntity, bool>>? whereExpression = null,
    CancellationToken cancellationToken = default)
        {
            var query = _dbContext.Queryable<TEntity>();
            if (whereExpression is not null)
                query = query.Where(whereExpression);
            return await query.MaxAsync(maxExpression);
        }



        public async Task<List<TResult>> GetListSelectResultAsync<TResult>(
            Expression<Func<TEntity, TResult>> selectExpression,
            Expression<Func<TEntity, bool>>? whereExpression = null,
            Expression<Func<TEntity, object>>? orderExpression = null,
            OrderByType orderByType = OrderByType.Asc,
            CancellationToken cancellationToken = default)
        {
            var queryable = _dbContext.Queryable<TEntity>();
            if (whereExpression is not null)
                queryable = queryable.Where(whereExpression);
            if (orderExpression is not null)
                queryable = queryable.OrderBy(orderExpression, orderByType);

            return await queryable.Select(selectExpression).ToListAsync();
        }


        public async Task<TResult> GetSeleteFirstAsync<TResult>(Expression<Func<TEntity, Boolean>> whereExpression,
            Expression<Func<TEntity, TResult>> selectExpression, CancellationToken cancellationToken = default)
        {
            return await _dbContext.Queryable<TEntity>().Where(whereExpression).Select(selectExpression).FirstAsync();
        }


        public async Task<List<TResult>> GetListAsync<T, TResult>(Expression<Func<T, Boolean>> whereExpression,
            Expression<Func<T, TResult>> selectExpression)
        {
            return await _dbContext.Queryable<T>().Where(whereExpression).Select(selectExpression).ToListAsync();
        }

        public async Task<List<TResult>> GetPagesListReturnTResult<TResult>(int pageIndex, int pageSize,
            RefAsync<int> totalNumber,
            Expression<Func<TEntity, TResult>> selectExpression,
            Expression<Func<TEntity, bool>>? whereExpression = null,
            Expression<Func<TEntity, object>>? orderExpression = null,
            OrderByType orderByType = OrderByType.Asc,
            CancellationToken cancellationToken = default)
        {
            var query = new List<TEntity>();
            var sugarQueryable = _dbContext.Queryable<TEntity>();
            if (whereExpression is not null)
                sugarQueryable = sugarQueryable.Where(whereExpression);
            if (orderExpression is not null)
                sugarQueryable = sugarQueryable.OrderBy(orderExpression, orderByType);
            return await sugarQueryable.Select(selectExpression).ToPageListAsync(pageIndex, pageSize, totalNumber);
        }


        public async Task<List<TEntity>> GetPagesList(int pageIndex, int pageSize, RefAsync<int> totalNumber,
            Expression<Func<TEntity, bool>>? whereExpression = null,
            Expression<Func<TEntity, object>>? orderExpression = null,
            OrderByType orderByType = OrderByType.Asc,
            CancellationToken cancellationToken = default)
        {
            var query = new List<TEntity>();
            var sugarQueryable = _dbContext.Queryable<TEntity>();
            if (whereExpression is not null)
                sugarQueryable = sugarQueryable.Where(whereExpression);
            if (orderExpression is not null)
                sugarQueryable = sugarQueryable.OrderBy(orderExpression, orderByType);
            return await sugarQueryable.ToPageListAsync(pageIndex, pageSize, totalNumber);
        }


        public async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>>? whereExpression = null,
            Expression<Func<TEntity, object>>? orderExpression = null,
            OrderByType orderByType = OrderByType.Asc,
            CancellationToken cancellationToken = default)
        {
            var sugarQueryable = _dbContext.Queryable<TEntity>();
            if (whereExpression is not null)
                sugarQueryable = sugarQueryable.Where(whereExpression);
            if (orderExpression is not null)
                sugarQueryable = sugarQueryable.OrderBy(orderExpression, orderByType);
            return await sugarQueryable.ToListAsync();
        }


        public async Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> whereExpression,
            CancellationToken cancellationToken = default)
        {
            return await _dbContext.GetSimpleClient<TEntity>().GetFirstAsync(whereExpression);
        }


        public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> whereExpression,
            CancellationToken cancellationToken = default)
        {
            return await _dbContext.GetSimpleClient<TEntity>().IsAnyAsync(whereExpression);
        }

        public async Task<bool> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entity);
            return await _dbContext.Insertable<TEntity>(entity).ExecuteCommandAsync() > 0;
        }


        public async Task<bool> InsertRangeAsync(List<TEntity> entitys,
            CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entitys);
            return await _dbContext.Insertable<TEntity>(entitys).ExecuteCommandAsync() > 0;
        }


        public async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression,
            CancellationToken cancellationToken = default)
        {
            return await _dbContext.Deleteable<TEntity>().Where(whereExpression).ExecuteCommandAsync() > 0;
        }

        public async Task<bool> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entity);
            return await _dbContext.Deleteable<TEntity>(entity).ExecuteCommandAsync() > 0;
        }

        public async Task<bool> DeleteRangeAsync(List<TEntity> entitys,
            CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entitys);
            return await _dbContext.Deleteable<TEntity>(entitys).ExecuteCommandAsync() > 0;
        }





        public async Task<bool> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entity);
            return await _dbContext.Updateable<TEntity>(entity).ExecuteCommandAsync() > 0;
        }

        public async Task<bool> UpdateWithoptLockAsync(TEntity entity,
            CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entity);
            return await _dbContext.Updateable<TEntity>(entity).ExecuteCommandWithOptLockAsync(true) > 0;
        }


        public async Task<bool> UpdateRangeAsync(List<TEntity> entitys,
            CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entitys);
            return await _dbContext.Updateable<TEntity>(entitys).ExecuteCommandAsync() > 0;
        }

        public async Task<bool> UpdateColumnsAsync(TEntity entity, Expression<Func<TEntity, object>> columns,
            CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entity);
            return await _dbContext.Updateable<TEntity>(entity).UpdateColumns(columns).ExecuteCommandAsync() > 0;
        }

        public async Task<bool> UpdateColumnsRangeAsync(List<TEntity> entity,
            Expression<Func<TEntity, object>> columns, CancellationToken cancellationToken = default)
        {
            await PushDomainEvent(entity);
            return await _dbContext.Updateable<TEntity>(entity).UpdateColumns(columns).ExecuteCommandAsync() > 0;
        }

        public async Task<TEntity> GetByIdAsync(object id, CancellationToken cancellationToken = default)
        {
            return await _dbContext.GetSimpleClient<TEntity>().GetByIdAsync(id);
        }

        protected async Task PushDomainEvent(TEntity entity)
        {
            if (entity.DomainEvents != null)
            {
                foreach (var notification in entity.DomainEvents)
                {
                    await _mediator.Publish(notification);
                }

                entity.ClearDomainEvents();
            }
        }

        protected async Task PushDomainEvent(List<TEntity> entitys)
        {
            foreach (var entity in entitys)
            {
                if (entity.DomainEvents != null)
                {
                    foreach (var notification in entity.DomainEvents)
                    {
                        await _mediator.Publish(notification);
                    }

                    entity.ClearDomainEvents();
                }
            }
        }
    }

    public class Repository<TEntity, TKey>
        : Repository<TEntity>, IRepository<TEntity, TKey>
        where TEntity : Entity<TKey>, new()
    {
        public Repository(ISqlSugarClient dbContext, IMediator mediator) : base(
            dbContext, mediator)
        {
        }

        public async Task<TEntity> GetByIdAsync(TKey id, CancellationToken cancellationToken = default)
        {
            return await base._dbContext.GetSimpleClient<TEntity>().GetByIdAsync(id);
        }

        public async Task<bool> SoftDeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            entity.Delete();
            await PushDomainEvent(entity);
            return await base._dbContext.Updateable<TEntity>(entity)
                .UpdateColumns(s => new { s.IsDelete, s.DeleteById, s.DeleteTime }).ExecuteCommandAsync() > 0;
        }

        public async Task<bool> SoftDeleteRangeAsync(List<TEntity> entitys,
            CancellationToken cancellationToken = default)
        {
            entitys.ForEach(entity => entity.Delete());
            await PushDomainEvent(entitys);
            return await base._dbContext.Updateable<TEntity>(entitys)
                .UpdateColumns(s => new { s.IsDelete, s.DeleteById, s.DeleteTime }).ExecuteCommandAsync() > 0;
        }
    }
}